/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    dma.h
 *  @brief   DMA header file
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#ifndef __DMA_H__
#define __DMA_H__

#include <stdint.h>
#include "types.h"
#include "io.h"
#include "bits.h"
#include "mem_map_table.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct chan_reg dma_chan_reg_t, *dma_chan_reg_t_ptr;
typedef struct chip_reg dma_chip_reg_t, *dma_chip_reg_t_ptr;
typedef struct slave_cfg slave_cfg_t, *slave_cfg_t_ptr;

enum dma_chan_nr {
	DMA_CHAN_NR_0,
	DMA_CHAN_NR_1,
	DMA_CHAN_NR_2,
	DMA_CHAN_NR_3,
	DMA_CHAN_NR_4,
	DMA_CHAN_NR_5,
	DMA_CHAN_NR_6,
	DMA_CHAN_NR_7,

	DMA_CHAN_NR_MAX,
};

#define DMA_BASE_ADDR                   MEM_MAP_DMAC_BASE_ADDR
#define DMA_CHAN_REG_LEN                (0x100UL)
#define DMA_CHIP_REG_OFFSET             (DMA_CHAN_NR_MAX * DMA_CHAN_REG_LEN)

#define DMA_CHAN_REG_PTR(chan)          ((dma_chan_reg_t_ptr)(DMA_BASE_ADDR + (chan * DMA_CHAN_REG_LEN)))
#define DMA_CHIP_REG_PTR()              ((dma_chip_reg_t_ptr)(DMA_BASE_ADDR + DMA_CHIP_REG_OFFSET))

#define XFER_LEN_POS                    0
#define XFER_LEN                        16

#define CMD_LAST                        BIT(1)
#define CMD_NEXT_ADDR_POS               4
#define CMD_NEXT_ADDR_LEN               28

#define RD_BURST_SIZE_POS               0
#define RD_BURST_SIZE_LEN               8
#define RD_INCR                         BIT(12)
#define SRC_IS_PERIPH                   BIT(18)
#define RX_SINGLE_BYTES_POS             20
#define RX_SINGLE_BYTES_LEN             4

#define WR_BURST_SIZE_POS               0
#define WR_BURST_SIZE_LEN               8
#define WR_INCR                         BIT(12)
#define DST_IS_PERIPH                   BIT(18)
#define TX_SINGLE_BYTES_POS             20
#define TX_SINGLE_BYTES_LEN             4

#define UNDEFINED_EN                    BIT(0)
#define TX_ACK_BYTES_POS                4
#define TX_ACK_BYTES_LEN                8
#define RX_ACK_BYTES_POS                12
#define RX_ACK_BYTES_LEN                8
#define CMD_PULSE_POS                   20
#define CMD_PULSE_LEN                   8

#define CHAN_ENABLE                     BIT(0)
#define CHAN_START                      BIT(0)
#define CHAN_ABORT                      BIT(0)
#define CHAN_SUSPEND                    BIT(0)

#define CHAN_RD_ACTIVE                  BIT(0)
#define CHAN_WR_ACTIVE                  BIT(1)
#define CHAN_RD_SUSPENDED               BIT(2)
#define CHAN_WR_SUSPENDED               BIT(3)

#define DMA_XFER_BUFFER_SIZE            (0x1000)

enum drq_line {
	DRQ_LINE_UART0,
	DRQ_LINE_UART1,
	DRQ_LINE_UART2,
	DRQ_LINE_UART3,
	DRQ_LINE_UART4,
	DRQ_LINE_I2C0,
	DRQ_LINE_I2C1,
	DRQ_LINE_SPI0,
	DRQ_LINE_SPI1,
	DRQ_LINE_SPI2,
	DRQ_LINE_QSPI,
};

/* Channel interrupt status */
enum chan_irq {
	CHAN_IRQ_NONE         = 0,
	CHAN_IRQ_END          = BIT(0),
	CHAN_IRQ_RD_TIMEOUT   = BIT(1),
	CHAN_IRQ_WR_TIMEOUT   = BIT(2),
	CHAN_IRQ_RESP_ERROR   = BIT(3),
	CHAN_IRQ_CMD_PULSE    = BIT(4),
	CHAN_IRQ_ERR_MASK     = GENMASK(4, 1),
	CHAN_IRQ_MASK         = GENMASK(4, 0),
	CHAN_IRQ_ALL          = GENMASK(4, 0),
};

/* Channel priority, 0 ~ 2 */
enum chan_priority {
	CHAN_PRIO_NORMAL,
	CHAN_PRIO_HIGH,
	CHAN_PRIO_TOP,
};

/* Memory/peripherals */
enum xfer_dir {
	XFER_DIR_MEM2MEM,
	XFER_DIR_MEM2DEV,
	XFER_DIR_DEV2MEM,
};

struct chan_reg {
	__IOM reg_t rd_addr;            /* 0x00 */
	__IOM reg_t wr_addr;            /* 0x04 */
	__IOM reg_t xfer_len;           /* 0x08 */
	__IOM reg_t next_addr;          /* 0x0c */
	__IOM reg_t rd_burst;           /* 0x10 */
	__IOM reg_t wr_burst;           /* 0x14 */
	__IOM reg_t ack_bytes;          /* 0x18 */
	__IM  reg_t rvsd0[9];
	__OM  reg_t ch_en;              /* 0x40 */
	__OM  reg_t ch_start;           /* 0x44 */
	__OM  reg_t ch_abort;           /* 0x48 */
	__IOM reg_t ch_suspend;         /* 0x4c */
	__IM  reg_t ch_active;          /* 0x50 */
	__IM  reg_t rd_cmd_stat;        /* 0x54 */
	__IM  reg_t wr_cmd_stat;        /* 0x58 */
	__IM  reg_t rd_len;             /* 0x5c */
	__IM  reg_t wr_len;             /* 0x60 */
	__IM  reg_t rvsd1[16];
	__IOM reg_t int_mask;           /* 0xa4 */
	__OM  reg_t int_clr;            /* 0xa8 */
	__OM  reg_t int_set;            /* 0xac */
	__IM  reg_t int_stat;           /* 0xb0 */
};

typedef struct chan_desc chan_desc_t, *chan_desc_t_ptr;

struct chan_desc {
	__IOM u32 sAddr;
	__IOM u32 dAddr;
	__IOM u32 buffSize;
	__IOM u32 nextAddr;
};

#define CHAN_WR_ACTIVE                  BIT(1)

#define WDT_LIMIT_POS                   0
#define WDT_LIMIT_LEN                   16

#define CH_END_DELAY_POS                0
#define CH_END_DELAY_LEN                8
#define DMA_LP_ENABLE                   BIT(8)
#define STARVATION_THRLD_POS            16
#define STARVATION_THRLD_LEN            16

#define MULTI_CHAN_START_POS            0
#define MULTI_CHAN_START_LEN            8

#define DMA_ABORT                       BIT(0)

#define CH_PRIOR_LEN                    2
#define CH_WEIGHT_LEN                   16

#define WEIGHT_UPDATE                   BIT(0)

#define PERIPH_LINE_LEN                 5

struct chip_reg {
	__IOM reg_t timeout;            /* 0x00 */
	__IOM reg_t int_delay;          /* 0x04 */
	__IM  reg_t rvsd0;
	__OM  reg_t chan_start;         /* 0x0c */
	__IM  reg_t rvsd1;
	__OM  reg_t abort;              /* 0x14 */
	__IM  reg_t rvsd2;
	__IOM reg_t priority;           /* 0x1c */
	__IOM reg_t weight[8];          /* 0x20 ~ 0x3c*/
	__OM  reg_t wght_update;        /* 0x40 */
	__IOM reg_t wr_periph[4];       /* 0x44 ~ 0x50 */
	__IOM reg_t rd_periph[4];       /* 0x54 ~ 0x60 */
	__IM  reg_t active;             /* 0x64 */
	__IM  reg_t status;             /* 0x68 */
};

/* Maximum Bus Width, 16 */
enum slave_buswidth {
	SLAVE_BUSWIDTH_UNDEFINED  = 0,
	SLAVE_BUSWIDTH_1_BYTE     = 1,
	SLAVE_BUSWIDTH_2_BYTES    = 2,
	SLAVE_BUSWIDTH_3_BYTES    = 3,
	SLAVE_BUSWIDTH_4_BYTES    = 4,
	SLAVE_BUSWIDTH_8_BYTES    = 8,
	SLAVE_BUSWIDTH_16_BYTES   = 16,
};

struct slave_cfg {
	enum xfer_dir dir;
	enum slave_buswidth srcAddrWidth;
	enum slave_buswidth dstAddrWidth;
	u32 txAck;
	u32 rxAck;
	u8 slaveId;
	u8 priority;
};

typedef void (*chan_xfer_cb_t)(int chan, u32 status);

void dma_init(void);
void dma_enter_lpmode(void);
int  dma_request_chan(int chan);

void dma_flush_lli_dcache(int chan);
void dma_flush_data_dcache(u32 addr, u32 len);
void dma_invalidate_data_dcache(u32 addr, u32 len);

int  dma_xfer_block_add(int chan, enum xfer_dir dir, u32 srcaddr, u32 dstaddr, u32 len);
void dma_register_cb(int chan, chan_xfer_cb_t cb);
void dma_cfg_slave(int chan, slave_cfg_t_ptr cfg);

void dma_xfer_start(int chan);
void dma_xfer_pending(int chan);
void dma_xfer_multi_chan_start(u8 mask);
void dma_periph_xfer_wait_finish(int chan);

void dma_xfer_abort(int chan);
void dma_xfer_suspend(int chan);
void dma_xfer_unsuspend(int chan);

int  dma_free_chan(int chan);

void dma_print_xfer_rate(int chan);

void dma_interrupt_handler(void *arg);

#ifdef __cplusplus
}
#endif

#endif /* __DMA_H__ */

